package com.rookie.tool.module.util.excel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.rookie.tool.module.util.excel.callback.ExcelDataCallback;
import com.rookie.tool.module.util.excel.handle.ExcelSheetWriteHandle;
import com.rookie.tool.module.util.excel.listener.ExcelDataListener;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author rookie
 */
public class ExcelProcessUtils {

    /**
     * 读取单个sheet
     *
     * @param filePath          文件本地路径
     * @param beanType          读取类的class
     * @param excelDataCallback 回调
     */
    public static <T> void readSingleSheet(String filePath, Class<T> beanType, ExcelDataCallback<T> excelDataCallback) {
        ExcelDataListener<T> listener = new ExcelDataListener<>(excelDataCallback);
        EasyExcel.read(filePath, beanType, listener).sheet().doRead();
    }

    /**
     * 读取单个sheet
     *
     * @param inputStream       文件流 MultipartFile file file.getInputStream()
     * @param beanType          读取类的class
     * @param excelDataCallback 回调
     */
    public static <T> void readSingleSheet(InputStream inputStream, Class<T> beanType, ExcelDataCallback<T> excelDataCallback) {
        ExcelDataListener<T> listener = new ExcelDataListener<>(excelDataCallback);
        EasyExcel.read(inputStream, beanType, listener).sheet().doRead();
    }

    /**
     * 读取单个sheet
     *
     * @param inputStream       文件流
     * @param beanType          读取类的class
     * @param excelDataCallback 回调
     * @param maxSize           执行一次overflowExecute的数据最大长度
     */
    public static <T> void readSingleSheet(InputStream inputStream, Class<T> beanType, ExcelDataCallback<T> excelDataCallback, int maxSize) {
        ExcelDataListener<T> listener = new ExcelDataListener<>(maxSize, excelDataCallback);
        EasyExcel.read(inputStream, beanType, listener).sheet().doRead();
    }

    /**
     * 读取所有sheet
     *
     * @param inputStream       文件流 MultipartFile file file.getInputStream()
     * @param beanType          读取类的class
     * @param excelDataCallback 回调 finishExecute会在每个sheet读取完毕后调用一次
     *                          所有sheet都会往同一个DataListener里面写
     */
    public static <T> void readAllSheet(InputStream inputStream, Class<T> beanType, ExcelDataCallback<T> excelDataCallback) {
        ExcelDataListener<T> listener = new ExcelDataListener<>(excelDataCallback);
        EasyExcel.read(inputStream, beanType, listener).doReadAll();
    }

    /**
     * 读取所有sheet
     *
     * @param inputStream       文件流
     * @param beanType          读取类的class
     * @param excelDataCallback 回调 finishExecute会在每个sheet读取完毕后调用一次
     *                          所有sheet都会往同一个DataListener里面写
     * @param maxSize           执行一次overflowExecute的数据最大长度
     */
    public static <T> void readAllSheet(InputStream inputStream, Class<T> beanType, ExcelDataCallback<T> excelDataCallback, int maxSize) {
        ExcelDataListener<T> listener = new ExcelDataListener<>(maxSize, excelDataCallback);
        EasyExcel.read(inputStream, beanType, listener).doReadAll();
    }

    /**
     * 写excel
     *
     * @param filePath  写到的文件路径
     * @param beanType  写类型
     * @param writeData 写List数据
     */
    public static <T> void writeSheet(String filePath, Class<T> beanType, List<T> writeData) {
        int maxSize = 100_0000;
        ExcelWriter excelWriter = EasyExcel.write(filePath, beanType).registerWriteHandler(new ExcelSheetWriteHandle()).build();
        maxProcess(writeData, p -> {
            writeData(p, writeData, maxSize, excelWriter);
            return 1;
        }, p -> {
            EasyExcel.write(filePath, beanType).registerWriteHandler(new ExcelSheetWriteHandle()).sheet("模板数据").doWrite(writeData);
            return 1;
        });
        endProcess(maxSize, writeData.size(), excelWriter);
    }

    public static <T> void writeSheet(String filePath, Class<T> beanType, List<T> writeData, List<SelectConfig> configs) {
        int maxSize = 100_0000;
        ExcelWriter excelWriter = buildPullBoxConfig(EasyExcel.write(filePath, beanType), configs).build();
        maxProcess(writeData, p -> {
            writeData(p, writeData, maxSize, excelWriter);
            return 1;
        }, p -> {
            buildPullBoxConfig(EasyExcel.write(filePath, beanType), configs).sheet("模板数据").doWrite(writeData);
            return 1;
        });

        endProcess(maxSize, writeData.size(), excelWriter);

    }

    /**
     * 写excel
     *
     * @param response   HttpServletResponse response
     * @param beanType   写类型
     * @param sheetTitle 底部标题
     * @param writeData  写List数据
     */
    public static <T> void writeSheet(HttpServletResponse response, Class<T> beanType, String fileName, String sheetTitle, List<T> writeData) {
        int maxSize = 100_0000;
        ExcelWriter excelWriter = EasyExcel.write(initFile(response, fileName), beanType).build();
        maxProcess(writeData, p -> {
            writeData(p, writeData, maxSize, excelWriter);
            return 1;
        }, p -> {
            EasyExcel.write(initFile(response, fileName), beanType).sheet(sheetTitle).doWrite(writeData);
            return 1;
        });
        endProcess(maxSize, writeData.size(), excelWriter);
    }

    private static <T> void maxProcess(List<T> writeData, Function<Integer, Integer> overFunction, Function<Integer, Integer> function) {
        int maxSize = 100_0000;
        int sheetNo = 0;
        if (writeData.size() > maxSize) {
            int times = writeData.size() / maxSize;
            if (writeData.size() % maxSize != 0) {
                times++;
            }
            for (int i = 0; i < times; i++) {
                overFunction.apply(sheetNo);
                sheetNo++;
            }
        } else {
            function.apply(sheetNo);
        }

    }

    private static <T> void writeData(int sheetNo, List<T> dataList, int maxSize, ExcelWriter excelWriter) {
        // 这里注意 如果同一个sheet只要创建一次
        WriteSheet writeSheet = EasyExcel.writerSheet(sheetNo, "模板数据" + sheetNo).build();
        List<T> data = dataList.stream().skip((long) maxSize * sheetNo).limit(maxSize).collect(Collectors.toList());
        excelWriter.write(data, writeSheet);
    }

    /**
     * 写excel
     *
     * @param response   HttpServletResponse response
     * @param beanType   写类型
     * @param sheetTitle 底部标题
     * @param writeData  写List数据
     * @param configs    下拉框配置
     */
    public static <T> void writeSheet(HttpServletResponse response, Class<T> beanType, String fileName, String sheetTitle, List<T> writeData, List<SelectConfig> configs) {
        buildPullBoxConfig(EasyExcel.write(initFile(response, fileName), beanType), configs).sheet(sheetTitle).doWrite(writeData);
    }

    private static OutputStream initFile(HttpServletResponse response, String fileName) {
        try {
            fileName = URLEncoder.encode(fileName, "UTF-8");
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            //防止导入excel文件名中文不乱码
            response.setHeader("Content-disposition", "attachment;fileName=" + fileName + ".xlsx" + ";fileName*=utf-8''" + fileName + ".xlsx");
            return response.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("请求异常");
        }
    }

    private static ExcelWriterBuilder buildPullBoxConfig(ExcelWriterBuilder write, List<SelectConfig> configs) {
        if (!ObjectUtils.isEmpty(configs)) {
            ExcelSheetWriteHandle excelSheetWriteHandle = new ExcelSheetWriteHandle();
            excelSheetWriteHandle.setSelectConfig(configs);
            return write.registerWriteHandler(excelSheetWriteHandle);
        }
        return write;
    }

    private static void endProcess(int maxSize, int dataSize, ExcelWriter excelWriter) {
        if (maxSize < dataSize) {
            excelWriter.finish();
        }
    }
}
